﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Skyline;
using QuantBox.XApi;

namespace QuantBox
{
    using SmartQuant;

    internal static class TypeConvert
    {
        public static string GetInstrumentTypeName(InstrumentType? type)
        {
            if (!type.HasValue) {
                return string.Empty;
            }

            switch (type) {
                case InstrumentType.Stock:
                    return DataCenterConst.Stock;
                case InstrumentType.Future:
                    return DataCenterConst.Future;
                case InstrumentType.ETF:
                    return DataCenterConst.Etf;
                case InstrumentType.Index:
                    return DataCenterConst.Index;
                case InstrumentType.Option:
                    return DataCenterConst.Option;
                case InstrumentType.FutureOption:
                    return DataCenterConst.FutureOption;
                default:
                    return string.Empty;
            }
        }

        public static InstrumentType ToInstrumentType(string type)
        {
            switch (type) {
                case DataCenterConst.Stock:
                    return InstrumentType.Stock;
                case DataCenterConst.Future:
                    return InstrumentType.Future;
                case DataCenterConst.Index:
                    return InstrumentType.Index;
                case DataCenterConst.Etf:
                    return InstrumentType.ETF;
                case DataCenterConst.Option:
                    return InstrumentType.Option;
                case DataCenterConst.IndexOption:
                case DataCenterConst.FutureOption:
                    return InstrumentType.FutureOption;
                default:
                    return InstrumentType.Stock;
            }
        }

        public static Instrument[] ToInstrument(InstrumentData[] items, byte providerId)
        {
            return items.Select(n => ToInstrument(n, providerId)).ToArray();
        }

        public static Instrument ToInstrument(InstrumentData data, byte providerId)
        {
            var type = ToInstrumentType(data.Type);
            var inst = new Instrument(type, data.Id);
            if (type == InstrumentType.FutureOption || type == InstrumentType.Option) {
                inst.PutCall = GetPutCall(data.CallOrPut);
                inst.Strike = data.RoundLot;
            }
            inst.AltId.Add(new AltId(providerId, data.ExchangeCode ?? data.Id, data.Exchange));
            inst.Description = data.Symbol;
            inst.CurrencyId = CurrencyId.CNY;
            inst.Factor = data.Factor;
            inst.Margin = data.MarginRate;
            inst.Maturity = data.MaturityDate;
            inst.Exchange = data.Exchange;
            inst.TickSize = data.TickSize;
            inst.PriceFormat = "F" + Utility.GetPrecision(data.TickSize);
            return inst;
        }

        private static PutCall GetPutCall(string callOrPut)
        {
            return callOrPut == "认购" ? PutCall.Call : PutCall.Put;
        }

        public static Bar[] ToBar(IEnumerable<BarDataField> items, Instrument inst, long barSize)
        {
            return items.Select(n => ToBar(n, inst, barSize)).ToArray();
        }

        private static Bar ToBar(BarDataField data, Instrument instrument, long barSize)
        {
#if Compatibility
            var bar = OpenQuant.Helper.NewBar(data.OpenDateTime,
                data.CloseDateTime,
                0,
                instrument.Id,
                BarType.Time,
                barSize,
                data.Open,
                data.High,
                data.Low,
                data.Close,
                data.Volume,
                data.OpenInterest);
#else
            var bar = new Bar(data.OpenDateTime,
                data.CloseDateTime,
                0,
                instrument.Id,
                BarType.Time,
                barSize,
                data.Open,
                data.High,
                data.Low,
                data.Close,
                data.Volume,
                data.OpenInterest);
#endif
            if (instrument.Exchange != ExchangeNames.CZCE) {
                data.Turnover /= instrument.Factor;
            }
            bar.SetTurnover(data.Turnover);
            if (bar.Size == QuantBoxConst.DayBarSize) {
                bar.SetSettle(data.Settle);
                bar.SetPreSettle(data.PreSettle);
                bar.SetPreClose(data.PreClose);
                bar.SetSymbol(data.Symbol);
            }
            return bar;
        }

        internal static Trade[] ToTrade(DepthMarketDataField[] ticks, Instrument instrument)
        {
            return ticks.Select(n => ToTrade(n, instrument)).ToArray();
        }

        private static Trade ToTrade(DepthMarketDataField tick, Instrument instrument)
        {
            var datetime = tick.ExchangeDateTime();
#if Compatibility
            var trade = OpenQuant.Helper.NewTick<Trade>(
                datetime,
                datetime,
                0,
                instrument.Id,
                tick.LastPrice,
                (int)tick.Volume);
#else
            var trade = new Trade(
                datetime,
                datetime,
                0,
                instrument.Id,
                tick.LastPrice,
                (int)tick.Volume);
#endif
            //if (instrument.Type == InstrumentType.Option) {
            //    trade.Price = trade.Price / instrument.Factor;
            //}
            if (instrument.Exchange != ExchangeNames.CZCE) {
                tick.Turnover /= instrument.Factor;
            }
            trade.SetMarketData(tick.Turnover, tick.OpenInterest);
            return trade;
        }

        internal static DataObject[] ToTradeAndQuote(DepthMarketDataField[] ticks, Instrument instrument)
        {
            var list = new List<DataObject>();
            foreach (var tick in ticks) {
                var (ask, bid) = ToAskBid(tick, instrument);
                if (ask != null) {
                    list.Add(ask);
                }
                if (bid != null) {
                    list.Add(bid);
                }
                list.Add(ToTrade(tick, instrument));
            }
            return list.ToArray();
        }

        private static Quote CreateQuote(Bid bid, Ask ask)
        {
            if (ask == null || bid == null) {
                var q = new Quote(new Bid(), new Ask());
                var fields = typeof(Quote).GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
                foreach (var field in fields) {
                    if (field.FieldType == typeof(Ask)) {
                        field.SetValue(q, ask);
                    }
                    else if (field.FieldType == typeof(Bid)) {
                        field.SetValue(q, bid);
                    }
                }
                q.DateTime = bid?.DateTime ?? ask.DateTime;
                return q;
            }
            return new Quote(bid, ask);
        }

        internal static DataObject[] ToQuote(DepthMarketDataField[] ticks, Instrument instrument)
        {
            var list = new List<DataObject>();
            foreach (var tick in ticks) {
                var (ask, bid) = ToAskBid(tick, instrument);
                list.Add(CreateQuote(bid, ask));
            }
            return list.ToArray();
        }

        internal static DataObject[] ToQuote(DepthMarketDataField[] ticks, Instrument instrument, byte dataType)
        {
            var list = new List<DataObject>();
            foreach (var tick in ticks) {
                var (ask, bid) = ToAskBid(tick, instrument);
                if (ask != null && dataType == EventType.Ask) {
                    list.Add(ask);
                }
                if (bid != null && dataType == EventType.Bid) {
                    list.Add(bid);
                }
            }
            return list.ToArray();
        }

        private static ValueTuple<Ask, Bid> ToAskBid(DepthMarketDataField tick, Instrument instrument)
        {
            var datetime = tick.ExchangeDateTime();
            Bid bid = null;
            if (tick.Bids[0].Size > 0) {
#if Compatibility
                bid = OpenQuant.Helper.NewTick<Bid>(
                    datetime,
                    datetime,
                    0,
                    instrument.Id,
                    tick.Bids[0].Price,
                    tick.Bids[0].Size);
#else
                bid = new Bid(
                    datetime,
                    datetime,
                    0,
                    instrument.Id,
                    tick.Bids[0].Price,
                    tick.Bids[0].Size);
#endif
                //if (instrument.Type == InstrumentType.Option) {
                //    bid.Price = bid.Price / instrument.Factor;
                //}
            }
            else {
                bid = new Bid(datetime, datetime, 0, instrument.Id, 0, 0);
            }
            Ask ask = null;
            if (tick.Asks[0].Size > 0) {
#if Compatibility
                ask = OpenQuant.Helper.NewTick<Ask>(
                    datetime,
                    datetime,
                    0,
                    instrument.Id,
                    tick.Asks[0].Price,
                    tick.Asks[0].Size);
#else
                ask = new Ask(
                    datetime,
                    datetime,
                    0,
                    instrument.Id,
                    tick.Asks[0].Price,
                    tick.Asks[0].Size);
#endif
                //if (instrument.Type == InstrumentType.Option) {
                //    ask.Price = ask.Price / instrument.Factor;
                //}
            }
            else {
                ask = new Ask(datetime, datetime, 0, instrument.Id, 0, 0);
            }

            return (ask, bid);
        }
    }
}
